package edu.kit.aifb.ruleintegrator.rule;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.collections.map.MultiValueMap;
import org.semanticweb.yars.nx.Resource;
import org.semanticweb.yars.nx.Variable;

import edu.kit.aifb.ruleintegrator.bayes.network.NetworkManager;
import edu.kit.aifb.ruleintegrator.repository.ClassStats;
import edu.kit.aifb.ruleintegrator.repository.RelationStats;
import edu.kit.aifb.ruleintegrator.repository.RepositoryStatistics;
import edu.kit.aifb.ruleintegrator.rule.metric.CosineFunction;
import edu.kit.aifb.ruleintegrator.rule.metric.Function;
import edu.kit.aifb.ruleintegrator.rule.metric.JaccardFunction;
import edu.kit.aifb.ruleintegrator.util.RuleException;

public class StructureManager {
	private Function[] functions = {new JaccardFunction(),
			new CosineFunction()};

	private Map<Resource,Predicate> m_predicates;
	// A predicate can have more than one rules
	private MultiValueMap m_rules=MultiValueMap.decorate(new LinkedMap());
//	private Map<Predicate,Rule> m_rules;
	private NetworkManager m_manager;
	
	public StructureManager(NetworkManager netManager){
		this.m_predicates=new HashMap<Resource, Predicate>();
		this.m_manager=netManager;
//		this.m_rules=new HashMap<Predicate,Rule>();
	}
	
	public Predicate createRelationPredicate(Resource relRes){
		if(m_predicates.get(relRes)!=null)
			return m_predicates.get(relRes);
		RelationPredicate pred=new RelationPredicate(relRes);
		this.m_predicates.put(relRes, pred);
		return pred;		
	}
	
	public Predicate createClassPredicate(Resource clsRes){
		if(m_predicates.get(clsRes)!=null)
			return m_predicates.get(clsRes);
		ClassPredicate pred=new ClassPredicate(clsRes);
		this.m_predicates.put(clsRes, pred);
		return pred;		
	}
	
	public Rule proposeRandomRule(Predicate head){
		if(head instanceof RelationPredicate){
			
		}
		// TODO define mapping for rule
		Rule rule=new Rule(new RulePredicate(head, null));
		return rule;
		
		
	}
	
	
	public void initializeRules() throws RuleException{

		RepositoryStatistics rs=this.m_manager.getRepositoryStatistics();
		
		
		for(Predicate pred:m_predicates.values()){
			if(pred instanceof ClassPredicate){
				// cast predicate to class predicate 
				ClassPredicate cp=(ClassPredicate)pred;				
				ClassStats cs=rs.getClassStats(cp.getClassResource());
				//signature mapping from class variable to rule variable
				SignatureMapping sm=new SignatureMapping(cp);
				//create head predicate
				RulePredicate head=new RulePredicate(cp, sm);
				//create rule
				Rule rule=new Rule(head);
				//create new rule variable in the signature of the rule with a unique id
				Variable ruleVar=Signature.createNewVariable();
				rule.getSignature().addVariable(ruleVar);
				//map the class variable to rule variable
				sm.map(cp.getVariable(), ruleVar);
				
				for(Resource res:cs.getRelations()){
					RelationPredicate relPred=(RelationPredicate)this.m_predicates.get(res);
					SignatureMapping sm2=new SignatureMapping(relPred);
					//map the source variable of relation to rule variable
					sm2.map(relPred.getSourceVariable(),ruleVar);
					// create a free auxillary variable for target
					Variable auxVar=Signature.createNewVariable();
					sm2.map(relPred.getTargetVariable(), auxVar);
					rule.getSignature().addVariable(auxVar);
					
					RulePredicate addableRP=new RulePredicate(relPred, sm2);
					rule.addAddablePredicate(addableRP);
					
				}
				m_rules.put(cp, rule);
				
			}else if(pred instanceof RelationPredicate){
				RelationPredicate rp=(RelationPredicate)pred;
				RelationStats rss=rs.getRelationStats(rp.getRelation());
				
				//signature mapping from relation variable to rule variable
				SignatureMapping sm=new SignatureMapping(rp);
				//create head predicate
				RulePredicate head=new RulePredicate(rp, sm);
				//create rule
				Rule rule=new Rule(head);
				//create new rule variables in the signature of the rule with unique ids
				Variable ruleVarS=Signature.createNewVariable();
				Variable ruleVarT=Signature.createNewVariable();
				
				rule.getSignature().addVariable(ruleVarS);
				rule.getSignature().addVariable(ruleVarT);
				//map the relation variables to rule variables
				sm.map(rp.getSourceVariable(), ruleVarS);
				sm.map(rp.getTargetVariable(), ruleVarT);
				
				for(Resource res:rss.getSourceRelations()){
					RelationPredicate relPred=(RelationPredicate)this.m_predicates.get(res);
					SignatureMapping sm2=new SignatureMapping(relPred);
					//map the source variable of relation to rule variable
					sm2.map(relPred.getSourceVariable(),ruleVarS);
					// create a free auxillary variable for target
					Variable auxVar=Signature.createNewVariable();
					sm2.map(relPred.getTargetVariable(), auxVar);
					rule.getSignature().addVariable(auxVar);
					
					RulePredicate addableRP=new RulePredicate(relPred, sm2);
					rule.addAddablePredicate(addableRP);					
				}
				
				for(Resource res:rss.getTargetRelations()){
					RelationPredicate relPred=(RelationPredicate)this.m_predicates.get(res);
					SignatureMapping sm2=new SignatureMapping(relPred);
					//map the source variable of relation to rule variable
					sm2.map(relPred.getTargetVariable(),ruleVarT);
					// create a free auxillary variable for source
					Variable auxVar=Signature.createNewVariable();
					sm2.map(relPred.getSourceVariable(), auxVar);
					rule.getSignature().addVariable(auxVar);
					
					RulePredicate addableRP=new RulePredicate(relPred, sm2);
					rule.addAddablePredicate(addableRP);					
				}
				
				for(Resource res:rss.getSourceClasses()){
					// TODO
				}
				
				for(Resource res:rss.getTargetClasses()){
					// TODO
				}
				
				
				
				
				
			}
				
			
			
		}
	}

	/**
	 * TODO Yongtao
	 * @return
	 */
	public List<Predicate> createMetricPredicates() {
		//
		
		RepositoryStatistics rs=this.m_manager.getRepositoryStatistics();
		
		for(Predicate pred:m_predicates.values()){
			
			RelationPredicate rp=(RelationPredicate)pred;
			RelationStats rss=rs.getRelationStats(rp.getRelation());
		}
		return null;
	}
	

}
